// Copyright © 1993,4 Apple Computer, Inc. All rights reserved
//Project Data for GoodForm

// application based constants
constant kAppSymbol := '|GoodForm:PIEDTS|;
constant kAppName := "GoodForm";
constant kPackageName := "GoodForm:PIEDTS";
constant kAppObject := '["Datum","Data"];
constant kAppAll:= "All Data";

// soup based constants
constant kSoupName := kPackageName;
constant kSoupIndexes := '[];

// a default soup entry
DefConst('kDefaultEntry, {text: "", labels: nil}); 


// GetAppParams constants
constant kMaxApplicationWidth:= 250;
constant kMaxApplicationHeight:= 336;

// Install and RemoveScripts

InstallScript := func(partFrame)
begin
   // register support for routing
	GetGlobals().routing.(kAppSymbol) := partFrame.theForm.entryRoutingFrame;
	
	// Register global find support
	AddArraySlot(findApps, kAppSymbol);
   
	// register filing support
	AddArraySlot(soupNotify, kSoupName);
	AddArraySlot(soupNotify, kAppSymbol);
end;


RemoveScript := func(packageFrame)
begin
	// Unregister global find support
	SetRemove(findApps, kAppSymbol);
   
	// un-register filing support
	local soupNotifyPos:= ArrayPos(soupNotify, kAppSymbol, 0, nil);
	ArrayRemoveCount(soupNotify, soupNotifyPos - 1, 2);
    
    // un-register routing support
    RemoveSlot(GetGlobals().routing, kAppSymbol);
end;
// ---- End Project Data ----


// ---- File GoodForm.t ----

// Before Script for "GoodFormBase"
// Copyright © 1993,4 Apple Computer, Inc. All rights reserved

GoodFormBase :=
   {
    PutAway:
      func(item)
      // this might be called while we are not running, hense the 
      // reason to check for the existance of uSoup.
      begin
      	// get the entry from the system
         local newEntry := item.body;
         local theSoup := nil;
      
         // make sure we have the correct folder 
         // for the labels slot
         CheckThatFolderExists(newEntry);
         if uSoup then
            theSoup := uSoup
         else 
         	theSoup := :RegisterCardSoup(kSoupName, kSoupIndexes, 
            						kAppSymbol, kAppObject);
         
         theSoup:AddToDefaultStore(newEntry);	// add the entry
         
         if not uSoup then
            :UnRegisterCardSoup(kSoupName);	// clean up
         
         BroadcastSoupChange(kSoupName);	// tell everyone we've changed
      end,
    SetTarget:
      func(entry)
      // used by several of our methods, this function
      // sets the target and adjusts both the cursor
      // and the display appropriately
      begin
      	if entry and cursor:Goto(entry) then
      		Target := entry
      	else
      		Target:= NIL;
      	
      	:UpdateFieldsFromEntry(target);
      end,
    viewSetupDoneScript:
      func()
      begin
      	// try to restore the old Target
      	local testTarget:= :GetLastTargetFromPrefsID();
      
      	if testTarget and cursor:Goto(testTarget) then
      		:SetTarget(testTarget)
      	else
      		begin
      			cursor:Reset();
      			:SetTarget(cursor:Entry())
      		end;
      	
      	// add support for Newton Connection Kit
      	:AddNCKSupport();
      end,
    SaveToSoup:
      func()
      // Save Entry to Soup
      begin
      	target.text := Clone(inputField.text);
      	EntryChange(target);
      	:SetDirty(nil);
      end,
    viewFormat: 524624,
    viewQuitScript:
      func()
      // Remember to unregister the soup, and zap the currently used 
      // references here, otherwise you get into trouble when removing
      // the PCMCIA card with the app.
      begin
      	// make sure we can restore the target if the card is removed
      	if Target then
      		prefsEntry:= EntryUniqueID(Target)
      	else
      		prefsEntry:= nil;
      	
      	// if the view is dirty, make sure the entry is changed
      	if dirty then
      		:SaveToSoup();
      
      	// nil out all of our references and un-register the card soup
      	call kUnRegisterCardSoupFunc with (kSoupName);
      	uSoup:= nil;
      	cursor:= nil;
      	Target:= nil;
      end,
    AppAll: kAppAll,
    target: nil,
    FolderChanged:
      func(soupname, oldLabel, newLabel)
      // basic task is to assign new values to
      // the 'labels slot in each check because
      // the user has changed the name of one
      // of the folders, NOTE this can be executed
      // when this app is not running.
      begin
      	local soup := GetUnionSoup(kSoupName);
         if soup then 
         	begin
            	local localCursor := query(soup, {type: 'index,
              	 validTest: func(entry)
               					begin
                  					return entry.labels = oldLabel;
               					end
           							});
      
            	// change the labels of each entry in the soup to newLabel
            	local localEntry := localCursor:Entry();
            	while localEntry do 
            		begin
               		localEntry.labels := newLabel;
               		EntryChange(localEntry);
               		localEntry := localCursor:Next();
            		end;
            		
            	// tell everyone else we've done it
      			BroadcastSoupChange(kSoupName);
         	end;
         	
         // redraw the folder tab if we are running
         if folderTab then
            folderTab:UpdateFilter(oldLabel, newLabel)
      end,
    viewFlags: 4,
    UndoDuplicateEntry:
      func(entry)
      // use the delete message and the DeleteAnEntry method
      // to crumple the view and remove the duplicate item
      begin
      	inputField:Delete('DeleteAnEntry, [entry]);
      end,
    viewIdleScript:
      func()
      begin
      	if dirty then
      		:SaveToSoup();
      	return 5000;	// we will get called every 5 seconds
      end,
    AddNCKSupport:
      func()
      begin
      	// get a reference to the Directory soup and another
      	// reference to a cursor that points to our 
      	// metadata entry in the directory
      	local soup := GetStores()[0]:GetSoup("Directory");
      	local q := Query(Soup, 
      							{type: 'index,
      								validTest: func(item)
      									StrEqual(item.soup, kSoupName)}
      							);
      	
      	// if our metadata is not entered there, enter it
      	// should really test version of meta data				
         if not q:Entry() then
      		begin
      			local nckMeta := Clone(nckMetaData);
            	soup:Add(nckMeta);
      		end
      end,
    DeleteActionScript:
      func(entry, targetView)
      begin
      	if Target then
      		begin
      			inputField:Delete('DeleteAnEntry, [entry]);	// Delete does a crumple	
      			AddUndoAction('undoDeleteEntry, [entry])
      		end
      	else
      		:Notify(kNotifyAlert, EnsureInternal(kAppName),
      			EnsureInternal("There is no Data to delete"));
      end,
    viewBounds: {top: 20, left: -2, right: 231, bottom: 280},
    appObject: kAppObject,
    FilingChanged:
      func()
      // basic task, decide if the target should be displayed 
      // using the current filter since the user has changed
      // the way the entry is filed
      begin
      	// make sure that the changes done to the labels slot
      	// are flushed from the cache
      	:SaveToSoup();
      	
      	if cursor:Goto(Target) then
      		begin
      			// do nothing, leave display alone
      		end
      	else
      		begin
      			cursor:Reset();
      			:SetTarget(cursor:Entry());	// this displays the target
      		end
      end,
    cursor:
      // keep track of the current cursor pointing at soup entries
      nil,
    FilterChanged:
      func()
      // basic task is to redraw the item(s) based 
      // on the new filter selected by the user
      begin
      	if Target and cursor:Goto(Target) then
      		begin
      			// do nothing, leave display alone
      		end
      	else
      		begin
      			// it must be filtered out, so display the first
      			// non-filtered entry
      			if dirty then
      				:SaveToSoup();
      			cursor:Reset();
      			:SetTarget(cursor:Entry());	// this displays the target
      		end
      end,
    labelsFilter: nil,
    ShowFoundItem:
      func(entry, finder)
      // shows the found item
      begin
         :SetTarget(entry);   // just go to the one selected, present it
      end,
    viewScrollDownScript:
      func()
      begin
      	// we use local variables in case the call to Next()
      	// fails, and we avoid restoring the cursor
      	local localCursor:= cursor:Clone();
      	local localEntry:= localCursor:Next();
      	if localEntry then
      		begin
      			if dirty then
      				:SaveToSoup();
      			:SetTarget(localEntry);
      		end
      	else
      		GetRoot():SysBeep()
      end,
    viewOverviewScript:
      func()
      begin
      	:Notify(kNotifyAlert, EnsureInternal(kAppName),
      				EnsureInternal("This application does not support an overview mode"));
      end,
    appSymbol: kAppSymbol,
    viewJustify: 16,
    nckMetaData:
      {	// The metadata description is rich and powerful.  Please refer to
      	// the documentation for more details which are beyond the scope of 
      	// this simple example.
      	
         soup: kSoupName,
       
         name: kAppName,
       
         version: 1,		// this code supports only version 1.0 of NCK
         					// but 2.0 is backward compatible
       
         defaultDefinitions: {
         	textEntry: {label: "Text Entry:", size: 100},
         },	 
       
         displayInfo: [	// each entry here is a slot name in the textFrame
         					// and the order here is the order they will appear
         					// in NCK's DataBrowser Window.
         	'textEntry,
         ],
       
         editInfo: [	// each entry here is a slot name in the textFrame
         				// and the order here is the order they will appear
         				// in NCK's Detail Window.
         	'textEntry,
         ],
       
         exportInfo: [	// ???
         	'textEntry,
         ],
       	
       	// The import() (from NCK to Newton) and the export() (from Newton
       	// to NCK) functions do the actual conversion of the data.  Note
       	// the structure of the textFrame used by NCK, it's easiest to
       	// see by looking in the export() function.  The "OriginalFrame"
       	// slot is actually storage for the soupFrame, and the other frames
       	// store conversions of those slots in the soupFrame to a textual
       	// format.
       	
       	import: func(textFrame)
         // converts text frame to soup entry
         	begin
       			if textFrame.originalFrame then
       				textFrame.originalFrame.text:= "LIKE WOW MAN" // textFrame.textEntry
       			else
       				textFrame.originalFrame:= {text: "totally cool"};// textFrame.textEntry};
       			
       			textFrame.originalFrame	// this is the soupFrame!!!
         	end,
       
         export: func(soupFrame)
         // converts soup entry to text frame for edit/display
      		begin
      			local textFrame;
      			textFrame:= {	originalFrame: soupFrame, 
      								textEntry: soupFrame.text};
        
         		textFrame;
         	end,
      },
    SetDirty:
      func(IsDirty)
      begin
      	dirty:= IsDirty;
      end,
    Find:
      func(what, results, scope, statusForm)
      begin
         if statusForm then
            statusForm:SetStatus("Searching in" && kAppName & $\u2026);
      
      	// application can be closed when this call is made, so
      	// get a local cursor to the soup
         local theSoup := GetUnionSoup(kSoupName);
         if theSoup then
         	begin
            	local theCursor := Query(theSoup, {type: 'text, text: what});
      
            	if theCursor:Entry() then 
            		begin
               		local theResult := 
               		{
                 	 		_proto: GetRoot().soupFinder,
                  		owner: self,
                  		title: kAppName,
                  		findType: 'text,
                  		findWords: [what],
                  		cursor: theCursor,
               		};
               		AddArraySlot(results,theResult);
            		end
         	end
      end,
    usoup: nil,
    viewScrollUpScript:
      func()
      begin
      	// we use local variables in case the call to Prev()
      	// fails, and we avoid restoring the cursor
      	local localCursor:= cursor:Clone();
      	local localEntry:= localCursor:Prev();
      	if localEntry then
      		begin
      			if dirty then
      				:SaveToSoup();
      			:SetTarget(localEntry);
      		end
      	else
      		GetRoot():SysBeep()
      end,
    DeleteAnEntry:
      func(entry)
      // remove the entry from our soup,
      // adjust the target and the display
      // note: all of our methods are built
      // to handle a NIL Target
      begin
      	EntryRemoveFromSoup(entry);
      	local newTarget:= cursor:Next();
      	if not newTarget then
      		newTarget:= cursor:Prev();
      	:SetTarget(newTarget);
      end,
    declareSelf: 'base,
    viewSetupFormScript:
      func()
      begin
      	uSoup := call kRegisterCardSoupFunc with 
      		(kSoupName,kSoupIndexes,kAppSymbol,kAppObject);
      	cursor := Query(uSoup, {type: 'index, 
      									ValidTest: func(entry) begin 
      										return labelsFilter = '_all or 
      											entry.labels = labelsFilter; end});
      	:SetupIdle(5000);
      	:ResizeToFitScreen();
      	TargetView:= self;
      end,
    DuplicateActionScript:
      func(entryToDuplicate, targetView)
      // copy the entry into our soup and
      // adjust the target and display
      begin
      	if entryToDuplicate then
      		begin
      			local newEntry:= EntryCopy(entryToDuplicate, uSoup);
      			:SetTarget(newEntry);
      			AddUndoAction('UndoDuplicateEntry, [newEntry])
      		end
      	else
      		GetRoot():SysBeep();
      end,
    dirty: nil,
    prefsEntry: nil,
    SetupRoutingSlip:
      func(fields)
      // required to support routing, this function stuffs
      // the slots of a frame passed to it by the system
      begin
      	fields.title:= "Beamed GoodFormDatum:" && Target.text
      end,
    GetLastTargetFromPrefsID:
      func()
      // uses a soup query to get an entry using the old target's _uniqueID
      begin
      	local localCursor := Query(uSoup, {
      						type: 'index, 
      						validTest: func(entry) 
      											begin 
      												return entry._uniqueID = prefsEntry;
      											end});
      	return localCursor:Entry();
      end,
    viewclass: 74,
    MakeNewEntry:
      func()
      // Create a new soup entry, the labels slot is used to support filing
      begin
        	if dirty then
      		:SaveToSoup();
      	local newEntry := Clone(kDefaultEntry);
      	newEntry.labels := if labelsFilter = '_all then nil else labelsFilter ;
        	uSoup:AddToDefaultStore(newEntry);
        	newEntry;
      end,
    FindSoupExcerpt:
      func(entry, finder)
      begin
      	inherited:FindSoupExcerpt(entry, finder);   
      	// call inherited version
      end,
    UpdateFieldsFromEntry:
      func(entry)
      // Update the view field with the entry from the soup
      // and if the entry is nil, display a notification
      begin
      	if entry then
      		begin
      			inputField:Show();
      			itemLabel:Show();
      			SetValue(inputField,'text, Clone(entry.text));
      			SetValue(infoField, 'text, "Entry No:" && entry._uniqueID)
      		end
      	else
      		begin
      			inputField:Hide();
      			itemLabel:Hide();
      		// Should really differentiate between no entries in folder
      		// 	and no entries at all (i.e., labelsFilter <> '_all and
      		//		no entries at all versus labelsFilter <> '_all and
      		//		entries in another folder
      			SetValue(infoField,'text,
      				if labelsFilter = '_all then
      					"There is no Data to display."
      				else
      					"There is no Data in this folder");
      		end;
      	:SetDirty(nil);
      end,
    debug: "GoodFormBase",
    undoDeleteEntry:
      func(entry)
      begin
      	uSoup:AddToDefaultStore(entry);
      	:SetTarget(entry);
      end,
    targetView: nil,
    ResizeToFitScreen:
      func()
      // this function uses the global function, GetAppParams()
      // to get the current size of the screen and adjusts our
      // base view's size accordingly
      begin
      	local params:= GetAppParams();  
      	self.viewBounds := RelBounds(
      		params.appAreaLeft,
      		params.appAreaTop,
      		MIN(kMaxApplicationWidth, params.appAreaWidth),
      		MIN(kMaxApplicationHeight, params.appAreaHeight)
      		);
      end,
    entryRoutingFrame:
      {
       	zap:	{
              	Title:	"Beam Datum",
              	routeForm:	'zapSlip,
              	},
              	
      	separator: NIL,
              
      	delete: {
      			title: "Delete",
      			routeScript: 'DeleteActionScript, 
              },
      
      	duplicate: {
      			title: "Duplicate",
      			routeScript: 'DuplicateActionScript,
              },
        
      	card: ROM_CardAction,
      }
   };

InfoLabel := /* child of GoodFormBase */
   {text: "Status:",
    viewBounds: {left: 5, top: 30, right: 90, bottom: 45},
    _proto: protoStaticText,
    debug: "InfoLabel"
   };



infoField := /* child of GoodFormBase */
   {
    text:
      "Tap the up and down arrows to scroll in the database.  Tap the \"New\" button to create a new record."
    ,
    viewBounds: {left: 0, top: 0, right: 220, bottom: 55},
    viewFormat: 336,
    viewJustify: 10368,
    _proto: protoStaticText,
    debug: "infoField"
   };
// View infoField is declared to GoodFormBase



ItemLabel := /* child of GoodFormBase */
   {text: "Current Item:",
    viewBounds: {left: 0, top: 12, right: 85, bottom: 27},
    viewJustify: 8398976,
    _proto: protoStaticText,
    debug: "ItemLabel"
   };
// View ItemLabel is declared to GoodFormBase



inputField := /* child of GoodFormBase */
   {viewFlags: 6657,
    viewFormat: 12625,
    viewlinespacing: 20,
    viewFont: userFont18,
    viewBounds: {left: 0, top: 0, right: 220, bottom: 102},
    viewJustify: 10368,
    viewChangedScript:
      func(slot, view)
      begin
      	:SetDirty(true);
      end,
    viewclass: 81,
    debug: "inputField"
   };
// View inputField is declared to GoodFormBase



_view000 := /* child of GoodFormBase */ {_proto: protoStatus};

_view001 := /* child of _view000 */ {_proto: protoActionButton};



_view002 := /* child of _view000 */
   {viewBounds: {left: -23, top: 2, right: -6, bottom: 15},
    _proto: protoFilingButton
   };



New := /* child of _view000 */
   {text: "New",
    buttonClickScript:
      func()
      begin
       :SetTarget(:MakeNewEntry());
      end,
    viewBounds: {left: 28, top: 2, right: 54, bottom: 15},
    viewJustify: 8388614,
    _proto: protoTextButton,
    debug: "New"
   };

// After Script for "New"
thisView := New;
// set the bounds of the button so that it is to the right
// of the clock and the correct height for the status bar
thisView.viewBounds := ButtonBounds(-(thisView.viewBounds.right - thisView.viewBounds.left));





folderTab := /* child of GoodFormBase */
   {_proto: protoFolderTab, debug: "folderTab"};
// View folderTab is declared to GoodFormBase







// ---- Beginning of section for non used Layout files ----

// End of output